package com.warski.wonderfires.dlna;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import com.warski.wonderfires.dlna.listener.DLANDeviceConnectListener;
import com.warski.wonderfires.dlna.listener.DLNAControlListener;
import com.warski.wonderfires.dlna.model.MediaInfo;
import com.warski.wonderfires.dlna.service.DLNABrowserService;
import com.warski.wonderfires.dlna.player.DLNAPlayer;
import com.warski.wonderfires.dlna.listener.DLNADeviceListener;
import com.warski.wonderfires.dlna.listener.DLNARegistryListener;
import com.warski.wonderfires.dlna.model.DeviceInfo;
import com.warski.wonderfires.dlna.type.EnumDeviceStatus;
import com.warski.wonderfires.utils.BasicUtils;
import com.warski.wonderfires.utils.FileUtils;
import com.warski.wonderfires.utils.Logger;
import com.warski.wonderfires.utils.network.EnumNetworkType;
import com.warski.wonderfires.utils.network.NetworkChangeReceiver;
import com.warski.wonderfires.utils.network.NetworkChangedListener;
import com.warski.wonderfires.dlna.nanohttpd.webserver.SimpleWebServer;
import org.fourthline.cling.android.AndroidUpnpService;
import org.fourthline.cling.model.action.ActionInvocation;
import org.fourthline.cling.support.model.PositionInfo;

import java.io.*;
import java.net.URLEncoder;
import java.util.Formatter;
import java.util.Locale;

/**
 * Created by xuhaiping on 2020/9/2.
 * dlna控制类，所有媒体推送通过该类操作
 */
public class DLNAManager {
    private final static String HttpServerPort = "8080";

    private static Context context;
    private static DLNARegistryListener registryListener;
    private static NetworkChangeReceiver networkChangeReceiver;
    private static AndroidUpnpService upnpService;
    private static ServiceConnection upnpServiceConnection;
    public static DLNAPlayer dlnaPlayer;

    /**
     * 初始化
     *
     * @param context
     */
    public static void init(Context context) {
        DLNAManager.context = context;

        startWatchDLNADevice();
        startWatchNetwork();
        startHttpServer();
        startUpnpService();
    }

    /**
     * 释放
     */
    public static void release() {
        disconnectDevice();
        stopUpnpService();
        stopHttpServer();
        stopWatchNetwork();
        stopWhatchDLNADevice();
    }

    private static void startWatchDLNADevice() {
        registryListener = new DLNARegistryListener();
    }

    private static void stopWhatchDLNADevice() {
        if (registryListener != null) {
            registryListener.setDeviceListener(null);
        }
    }

    public static void startSearch() {
        if (upnpService != null) {
            upnpService.getRegistry().addListener(registryListener);
            upnpService.getControlPoint().search();
        }
    }

    public static void stopSearch() {
        if (upnpService != null) {
            upnpService.getRegistry().addListener(registryListener);
            upnpService.getControlPoint().search();
        }
    }

    /**
     * 设置设备监听回调
     *
     * @param listener
     */
    public static void setDeviceListener(DLNADeviceListener listener) {
        if (registryListener != null) {
            registryListener.setDeviceListener(listener);
            if (upnpService != null) {
                listener.onDeviceListRefresh(upnpService.getRegistry().getDevices());
            }
        }
    }

    /**
     * 清除设备监听
     */
    public static void removeDeviceListener() {
        if (registryListener != null) {
            registryListener.setDeviceListener(null);
        }
    }

    private static void startWatchNetwork() {
        networkChangeReceiver = new NetworkChangeReceiver();
        networkChangeReceiver.registerReceiver(context);
        networkChangeReceiver.addListener(new NetworkChangedListener() {
            public void onNetworkChanged(EnumNetworkType networkType) {
                stopHttpServer();
                startHttpServer();
            }
        });
    }

    private static void stopWatchNetwork() {
        if (networkChangeReceiver != null) {
            networkChangeReceiver.unregisterReceiver(context);
        }
    }

    private static void startHttpServer() {
        try {
            final PipedOutputStream pipedOutputStream = new PipedOutputStream();
            System.setIn(new PipedInputStream(pipedOutputStream));

            String localIP = BasicUtils.getLocalIP(context);
            String localPath = getLocalStorePath();
            String[] args = {
                    "--host", localIP,
                    "--port", HttpServerPort,
                    "--dir", localPath
            };
            SimpleWebServer.startServer(args);
            Logger.info("Start local http server: %s %s", localIP, localPath);
        } catch (Exception e) {
            Logger.error("Start local http server failed");
            Logger.error(e);
        }
    }

    private static void stopHttpServer() {
        SimpleWebServer.stopServer();
    }

    private static void startUpnpService() {
        upnpServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                upnpService = (AndroidUpnpService) service;
                upnpService.getRegistry().addListener(registryListener);
                Logger.info("Upnp Service Connected");
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                upnpService.getRegistry().shutdown();
                upnpService = null;
                Logger.info("Upnp Service Disconnected");
            }

            @Override
            public void onBindingDied(ComponentName name) {
                upnpService.getRegistry().shutdown();
                upnpService = null;
                Logger.info("Upnp Service Shutdown");
            }
        };

        context.bindService(new Intent(context, DLNABrowserService.class), upnpServiceConnection, Context.BIND_AUTO_CREATE);
    }

    private static void stopUpnpService() {
        if (upnpService != null) {
            upnpService.getRegistry().removeListener(registryListener);
            upnpService.getRegistry().shutdown();
        }
        if (upnpServiceConnection != null) {
            context.unbindService(upnpServiceConnection);
        }
    }

    private static String localMediaPath2HttpServerUrl(String sourceUrl) {
        String httpSourceUrl = String.format("%s%s", getLocalHttpServerAddress(), sourceUrl.replace(getLocalStorePath(), ""));
        Logger.info("pushLocalMediaFile2HttpServer: ");
        Logger.info("LOCAL: %s", sourceUrl);
        try {
            final String[] urlSplits = httpSourceUrl.split("/");
            final String originFileName = urlSplits[urlSplits.length - 1];
            String fileName = originFileName;
            fileName = URLEncoder.encode(fileName, "UTF-8");
            fileName = fileName.replaceAll("\\+", "%20");
            httpSourceUrl = httpSourceUrl.replace(originFileName, fileName);
            Logger.info("HTTP : %s", httpSourceUrl);
        } catch (UnsupportedEncodingException e) {
            Logger.error(e);
        }
        return httpSourceUrl;
    }

    private static String pushAssetFile2HttpServer(String assetPath) {
        if (assetPath.startsWith("ASSET:")) {
            assetPath = assetPath.substring(6);
        }
        File outFile = new File(getLocalStorePath(), FileUtils.getAssetFilename(assetPath));

        InputStream in = FileUtils.readAssetAsInputStream(assetPath);
        OutputStream out = null;
        try {
            out = new FileOutputStream(outFile);
            byte[] buffer = new byte[1024];
            int read;
            while ((read = in.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
            return outFile.getAbsolutePath();
        } catch (Exception e) {
            Logger.error(e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {

            }
        }
        return null;
    }

    private static boolean isLocalMediaFile(String url) {
        if (url.startsWith(getLocalStorePath())) {
            return true;
        }
        return false;
    }

    private static String getLocalHttpServerAddress() {
        return String.format("http://%s:%s", BasicUtils.getLocalIP(context), HttpServerPort);
    }

    public static String getLocalStorePath() {
        return context.getExternalFilesDir(null).getAbsolutePath();
    }

    /**
     * 连接设备
     *
     * @param deviceInfo
     */
    public static void connectDevice(DeviceInfo deviceInfo, DLANDeviceConnectListener listener) {
        disconnectDevice();
        dlnaPlayer = new DLNAPlayer(context);
        dlnaPlayer.connect(deviceInfo, listener);
    }

    /**
     * 断开当前连接
     */
    public static void disconnectDevice() {
        if (dlnaPlayer != null) {
            dlnaPlayer.release();
            dlnaPlayer = null;
        }
    }

    /**
     * 获取当前连接的设备
     *
     * @return
     */
    public static DeviceInfo getConnectedDevice() {
        if (dlnaPlayer != null) {
            return dlnaPlayer.getCurrentDevice();
        }
        return null;
    }

    /**
     * 播放新的
     *
     * @param mediaInfo
     * @param listener
     */
    public static void play(MediaInfo mediaInfo, DLNAControlListener listener) {
        if (dlnaPlayer != null) {
            if (FileUtils.isAssetFile(mediaInfo.getUrl())) {
                mediaInfo.setUrl(pushAssetFile2HttpServer(mediaInfo.getUrl()));
            }
            if (isLocalMediaFile(mediaInfo.getUrl())) {
                mediaInfo.setUrl(localMediaPath2HttpServerUrl(mediaInfo.getUrl()));
            }

            dlnaPlayer.setDataSource(mediaInfo);
            dlnaPlayer.start(listener);
        }
    }

    /**
     * 继续播放
     *
     * @param listener
     */
    public static void play(final DLNAControlListener listener) {
        if (dlnaPlayer != null) {
            dlnaPlayer.play(listener);
        }
    }

    /**
     * 暂停
     *
     * @param listener
     */
    public static void pause(final DLNAControlListener listener) {
        if (dlnaPlayer != null) {
            dlnaPlayer.pause(listener);
        }
    }

    /**
     * 停止播放
     *
     * @param listener
     */
    public static void stop(final DLNAControlListener listener) {
        if (dlnaPlayer != null) {
            dlnaPlayer.stop(listener);
        }
    }

    /**
     * 是否正在播放
     */
    public static boolean isPlaying() {
        if (dlnaPlayer != null) {
            return dlnaPlayer.getCurrentDevice().getState() == EnumDeviceStatus.PLAY;
        }
        return false;
    }

    /**
     * 获取当前播放位置 秒
     * 有些设备性能差，获取时间返回有延迟
     *
     * @param listener
     */
    public static void getPosition(final DLNAControlListener listener) {
        if (dlnaPlayer != null) {
            dlnaPlayer.getPositionInfo(new DLNAControlListener() {
                public void onDLNASuccess(ActionInvocation invocation) {
                    if (listener != null) {
                        listener.onDLNASuccess(invocation);
                    }
                }

                public void onDLNAMessage(ActionInvocation invocation, Object... message) {
                    PositionInfo positionInfo = (PositionInfo) message[0];
                    if (listener != null) {
                        listener.onDLNAMessage(
                                invocation,
                                string2Seconds(positionInfo.getRelTime()),
                                positionInfo.getTrackDuration().compareTo(positionInfo.getRelTime()) == 0
                        );
                    }
                }

                public void onDLNAError(ActionInvocation invocation, String error) {
                    if (listener != null) {
                        listener.onDLNAError(invocation, error);
                    }
                }
            });
        }
    }

    /**
     * 指定播放
     *
     * @param sec
     * @param listener
     */
    public static void seekTo(int sec, final DLNAControlListener listener) {
        if (dlnaPlayer != null) {
            dlnaPlayer.seekTo(seconds2String(sec), listener);
        }
    }

    /**
     * 秒转00:00:00
     *
     * @param secs
     * @return
     */
    private static String seconds2String(int secs) {
        StringBuilder formatBuilder = new StringBuilder();
        Formatter formatter = new Formatter(formatBuilder, Locale.getDefault());
        int seconds = secs % 60;
        int minutes = (secs / 60) % 60;
        int hours = secs / 3600;
        formatBuilder.setLength(0);
        return formatter.format("%02d:%02d:%02d", hours, minutes, seconds).toString();
    }

    /**
     * 00:00:00转秒
     *
     * @param time
     * @return
     */
    private static int string2Seconds(String time) {
        String[] tmp = time.split(":");
        if (tmp.length < 3) {
            return 0;
        }
        int second = Integer.valueOf(tmp[0]) * 3600 + Integer.valueOf(tmp[1]) * 60 + Integer.valueOf(tmp[2]);
        return second;
    }
}
